using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class XEventArgs
{
    public float timestamp;

    /// <summary>
    /// 
    /// </summary>
    public XEventArgs()
    {
        timestamp = Time.time;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="exists"></param>
    public static implicit operator bool(XEventArgs exists)
    {
        return exists != null;
    }
}

/// <summary>
/// event callback
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public delegate bool EventCallback(XEventArgs args);

/// <summary>
/// event listener
/// </summary>
public class XEventModule : MonoBehaviour
{
    /// <summary>
    /// 
    /// </summary>
    protected XEventDispatcher dispatcher;

    /// <summary>
    /// 
    /// </summary>
    protected Dictionary<int,
        EventCallback> events = new Dictionary<int, EventCallback>();

    /// <summary>
    /// 
    /// </summary>
    protected bool active = false;
    
    /// <summary>
    /// 
    /// </summary>
    /// <param name="eventDispatcher"></param>
    public void SetDispatcher(XEventDispatcher eventDispatcher)
    {
        dispatcher = eventDispatcher;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public XEventDispatcher GetDispatcher()
    {
        return dispatcher;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <param name="complete"></param>
    public void Load(System.Type type, System.Action complete=null)
    {
        StartCoroutine(OnLoad(type, complete));
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <param name="complete"></param>
    /// <returns></returns>
    IEnumerator OnLoad(System.Type type, System.Action complete)
    {
        dispatcher.Load(type);
        yield return new WaitForEndOfFrame();

        complete?.Invoke();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <param name="complete"></param>
    public void Unload(System.Type type, System.Action complete=null)
    {
        StartCoroutine(OnUnload(type, complete));
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <param name="complete"></param>
    /// <returns></returns>
    IEnumerator OnUnload(System.Type type, System.Action complete)
    {
        dispatcher.Unload(type);
        yield return new WaitForEndOfFrame();

        complete?.Invoke();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="complete"></param>
    public void Load<T>(System.Action complete=null) where T : XEventModule
    {
        StartCoroutine(OnLoad<T>(complete));
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="complete"></param>
    /// <returns></returns>
    IEnumerator OnLoad<T>(System.Action complete) where T : XEventModule
    {
        dispatcher.Load<T>();

        yield return new WaitForEndOfFrame();

        complete?.Invoke();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="complete"></param>
    public void Unload<T>(System.Action complete=null) where T : XEventModule
    {
        XCoroutine.Run(OnUnload<T>(complete));
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="complete"></param>
    /// <returns></returns>
    IEnumerator OnUnload<T>(System.Action complete) where T : XEventModule
    {
        dispatcher.Unload<T>();

        yield return new WaitForEndOfFrame();

        complete?.Invoke();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="nEventId"></param>
    /// <param name="callback"></param>
    public void SubscribeEvent(int nEventId, EventCallback callback)
    {
        if (!events.ContainsKey(nEventId))
        {
            events.Add(nEventId, callback);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="nEventId"></param>
    /// <returns></returns>
    public bool HasEvent(int nEventId)
    {
        return events.ContainsKey(nEventId);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="nEventId"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    public bool FireEvent(int nEventId, XEventArgs args)
    {
        return events[nEventId](args);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="nEventId"></param>
    public void RemoveEvent(int nEventId)
    {
        if (events.ContainsKey(nEventId))
        {
            events.Remove(nEventId);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public void ResetEvent()
    {
        events.Clear();
    }

    /// <summary>
    /// 
    /// </summary>
    public virtual void Active()
    {
        if (!active)
        {
            active = true;
            gameObject.SetActive(true);

#if UNITY_EDITOR
            GLog.Log("<color=magenta> {0} Active!</color>", GetType().Name);
#endif

            OnActive();
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public bool IsActive()
    {
        return gameObject.activeSelf;
    }

    /// <summary>
    /// 
    /// </summary>
    public virtual void Detive()
    {
        if (active)
        {
            active = false;
            gameObject.SetActive(false);

#if UNITY_EDITOR
            GLog.Log("<color=magenta> {0} Detive!</color>", GetType().Name);
#endif

            OnDetive();
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public virtual void OnActive()
    {
    }

    /// <summary>
    /// 
    /// </summary>
    public virtual void OnDetive()
    {
    }
}
